home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 722 / 722.xpi / chrome / noscript.jar / content / noscript / HTTPS.js < prev    next >
Text File  |  2010-02-12  |  12KB  |  385 lines

  1. INCLUDE('STS', 'Cookie');
  2.  
  3. const HTTPS = {
  4.   secureCookies: false,
  5.   secureCookiesExceptions: null,
  6.   secureCookiesForced: null,
  7.   httpsForced: null,
  8.   httpsForcedExceptions: null,
  9.   
  10.   
  11.   
  12.   forceChannel: function(channel) {
  13.     return this.forceURI(channel.URI, function() { HTTPS.replaceChannel(channel); });
  14.   },
  15.   
  16.   replaceChannel: function(channel) {
  17.     if (ChannelReplacement.supported) {
  18.       IOUtil.runWhenPending(channel, function() {
  19.         // replacing a shortcut icon causes a cache-related crash like
  20.         // https://bugzilla.mozilla.org/show_bug.cgi?id=480352
  21.         // just abort it... 
  22.         try {
  23.          if (/\bimage\//.test(channel.getRequestHeader("Accept")) &&
  24.              !PolicyState.extract(channel) // favicons don't trigger content policies
  25.             ) {
  26.            HTTPS.log("Aborting shortcut icon " + channel.name + ", should be HTTPS!");
  27.            IOUtil.abort(channel);
  28.            return;
  29.          }
  30.         } catch(e) {}
  31.         var uri = channel.URI.clone();
  32.         uri.scheme = "https";
  33.         new ChannelReplacement(channel, uri).replace(true).open();
  34.       });
  35.       return true;
  36.     }
  37.     
  38.     this.log("Aborting redirection " + channel.name + ", should be HTTPS!");
  39.     IOUtil.abort(channel);
  40.     return false;
  41.   },
  42.   
  43.   forceURI: function(uri, fallback, ctx) {
  44.     if (this.mustForce(uri)) {
  45.       try {
  46.         
  47.         uri.scheme = "https";
  48.         
  49.         this.log("Forced URI " + uri.spec);
  50.         return true;
  51.         
  52.       } catch(e) {
  53.         
  54.         if (ctx && (ctx instanceof CI.nsIDOMHTMLImageElement || ctx instanceof CI.nsIDOMHTMLInputElement)) {
  55.           uri = uri.clone();
  56.           uri.scheme = "https";
  57.           Thread.asap(function() { ctx.src = uri.spec; });
  58.           
  59.           var msg = "Image HTTP->HTTPS redirection to " + uri.spec;
  60.           this.log(msg);  
  61.           throw msg;
  62.         }
  63.         
  64.         if (fallback && fallback()) {
  65.            this.log("Channel redirection fallback on " + uri.spec);
  66.            return true;
  67.         }
  68.         
  69.         this.log("Error trying to force https on " + uri.spec + ": " + e);
  70.       }
  71.     }
  72.     return false;
  73.   },
  74.   
  75.   mustForce: function(uri) {
  76.     return (uri.schemeIs("http") &&
  77.         (this.httpsForced && this.httpsForced.test(uri.spec) ||
  78.          STS.isSTSURI(uri)) &&
  79.           !(this.httpsForcedExceptions &&
  80.             this.httpsForcedExceptions.test(uri.spec)
  81.         ));
  82.   },
  83.   
  84.   log: function(msg) {
  85.     this.log = ns.getPref("https.showInConsole", true)
  86.       ? function(msg) { ns.log("[NoScript HTTPS] " + msg); }
  87.       : function(msg) {}
  88.       
  89.     return this.log(msg);
  90.   },
  91.   
  92.   onCrossSiteRequest: function(channel, origin, browser, rw) {
  93.     try {
  94.       this.handleCrossSiteCookies(channel, origin, browser);
  95.     } catch(e) {
  96.       this.log(e + " --- " + e.stack);
  97.     }
  98.   },
  99.   
  100.   registered: false,
  101.   handleSecureCookies: function(req) {
  102.   /*
  103.     we check HTTPS responses setting cookies and
  104.     1) if host is in the noscript.secureCookiesExceptions list we let
  105.      it pass through
  106.     2) if host is in the noscript.secureCookiesForced list we append a
  107.        ";Secure" flag to every non-secure cookie set by this response
  108.     3) otherwise, we just log unsafe cookies BUT if no secure cookie
  109.        is set, we patch all these cookies with ";Secure" like in #2.
  110.        However, if current request redirects (directly or indirectly)
  111.        to an unencrypted final URI, we remove our ";Secure" patch to
  112.        ensure compatibility (ref: mail.yahoo.com and hotmail.com unsafe
  113.        behavior on 11 Sep 2008)
  114.   */
  115.     
  116.     if (!this.secureCookies) return;
  117.     
  118.     var uri = req.URI;
  119.     
  120.     if (uri.schemeIs("https") &&
  121.         !(this.secureCookiesExceptions && this.secureCookiesExceptions.test(uri.spec)) &&
  122.         (req instanceof CI.nsIHttpChannel)) {
  123.       try {
  124.         var host = uri.host;
  125.         try {
  126.           var cookies = req.getResponseHeader("Set-Cookie");
  127.         } catch(mayHappen) {
  128.           return;
  129.         }
  130.         if (cookies) {
  131.           var forced = this.secureCookiesForced && this.secureCookiesForced.test(uri.spec);
  132.           var secureFound = false;
  133.           var unsafe = null;
  134.          
  135.           const rw = ns.requestWatchdog;
  136.           var browser = rw.findBrowser(req);
  137.           
  138.           if (!browser) {
  139.             if (ns.consoleDump) ns.dump("Browser not found for " + uri.spec);
  140.           }
  141.           
  142.           var unsafeMap = this.getUnsafeCookies(browser) || {};
  143.           var c;
  144.           for each (var cs in cookies.split("\n")) {
  145.             c = new Cookie(cs, host);
  146.             if (c.secure && c.belongsTo(host)) {
  147.               this.log("Secure cookie set by " + host + ": " + c);
  148.               secureFound = c;
  149.               delete unsafeMap[c.id];
  150.             } else {
  151.               if (!unsafe) unsafe = [];
  152.               unsafe.push(c);
  153.             }
  154.           }
  155.         
  156.           
  157.           if (unsafe && !(forced || secureFound)) {
  158.             // this page did not set any secure cookie, let's check if we already have one
  159.             secureFound = Cookie.find(function(c) {
  160.               return (c instanceof CI.nsICookie) && (c instanceof CI.nsICookie2)
  161.                 && c.secure && !unsafe.find(function(x) { return x.sameAs(c); })
  162.             });
  163.             if (secureFound) {
  164.               this.log("Secure cookie found for this host: " + Cookie.prototype.toString.apply(secureFound));
  165.             }
  166.           }
  167.           
  168.           if (secureFound && !forced) {
  169.             this.cookiesCleanup(secureFound);
  170.             return;
  171.           }
  172.           
  173.           if (!unsafe) return;
  174.  
  175.           var msg;
  176.           if (forced || !secureFound) {
  177.             req.setResponseHeader("Set-Cookie", "", false);
  178.             msg = forced ? "FORCED SECURE" : "AUTOMATIC SECURE";
  179.             forced = true;
  180.           } else {
  181.             msg = "DETECTED INSECURE";
  182.           }
  183.           
  184.           if (!this.registered) {
  185.             this.registered = true;
  186.             rw.addCrossSiteListener(this);
  187.           }
  188.           
  189.           this.setUnsafeCookies(browser, unsafeMap);
  190.           msg += " on https://" + host + ": ";
  191.           for each (c in unsafe) {
  192.             if (forced) {
  193.               c.secure = true;
  194.               req.setResponseHeader("Set-Cookie", c.source + ";Secure", true);
  195.               unsafeMap[c.id] = c;
  196.             }
  197.             this.log(msg + c);
  198.           }
  199.           
  200.         }
  201.       } catch(e) {
  202.         if (ns.consoleDump) ns.dump(e);
  203.       }
  204.     }
  205.   },
  206.   
  207.   handleCrossSiteCookies: function(req, origin, browser) {
  208.      
  209.     var unsafeCookies = this.getUnsafeCookies(browser);
  210.     if (!unsafeCookies) return;
  211.     
  212.     var uri = req.URI;
  213.     var dscheme = uri.scheme;
  214.     
  215.     var oparts = origin && origin.match(/^(https?):\/\/([^\/:]+).*?(\/.*)/);
  216.     if (!(oparts && /https?/.test(dscheme))) return; 
  217.     
  218.     var oscheme = oparts[1];
  219.     if (oscheme == dscheme) return; // we want to check only cross-scheme requests
  220.     
  221.     var dsecure = dscheme == "https";
  222.     
  223.     if (dsecure && !ns.getPref("secureCookies.recycle", false)) return;
  224.    
  225.     var dhost = uri.host;
  226.     var dpath = uri.path;
  227.     
  228.     var ohost = oparts[2];
  229.     var opath = oparts[3];
  230.     
  231.     var ocookieCount = 0, totCount = 0;
  232.     var dcookies = [];
  233.     var c;
  234.     
  235.     for (var k in unsafeCookies) {
  236.       c = unsafeCookies[k];
  237.       if (!c.exists()) {
  238.         delete unsafeCookies[k];
  239.       } else {
  240.         totCount++;
  241.         if (c.belongsTo(dhost, dpath) && c.secure != dsecure) { // either secure on http or not secure on https
  242.           dcookies.push(c);
  243.         }
  244.         if (c.belongsTo(ohost, opath)) {
  245.           ocookieCount++;
  246.         }
  247.       }
  248.     }
  249.     
  250.     if (!totCount) {
  251.       this.setUnsafeCookies(browser, null);
  252.       return;
  253.     }
  254.     
  255.     // We want to "desecurify" cookies only if cross-navigation to unsafe
  256.     // destination originates from a site sharing some secured cookies
  257.  
  258.     if (ocookieCount == 0 && !dsecure || !dcookies.length) return; 
  259.     
  260.     if (dsecure) {
  261.       this.log("Detected cross-site navigation with secured cookies: " + origin + " -> " + uri.spec);
  262.       
  263.     } else {
  264.       this.log("Detected unsafe navigation with NoScript-secured cookies: " + origin + " -> " + uri.spec);
  265.       this.log(uri.prePath + " cannot support secure cookies because it does not use HTTPS. Consider forcing HTTPS for " + uri.host + " in NoScript's Advanced HTTPS options panel.")
  266.     }
  267.     
  268.     var cs = CC['@mozilla.org/cookieService;1'].getService(CI.nsICookieService).getCookieString(uri, req);
  269.       
  270.     for each (c in dcookies) {
  271.       c.secure = dsecure;
  272.       c.save();
  273.       this.log("Toggled secure flag on " + c);
  274.     }
  275.  
  276.     if (cs) {
  277.       Array.prototype.push.apply(
  278.         dcookies, cs.split(/\s*;\s*/).map(function(cs) { var nv = cs.split("="); return { name: nv.shift(), value: nv.join("=") } })
  279.          .filter(function(c) { return dcookies.every(function(x) { return x.name != c.name }) })
  280.       );
  281.     }
  282.  
  283.     cs = dcookies.map(function(c) { return c.name + "=" + c.value }).join("; ");
  284.  
  285.     this.log("Sending Cookie for " + dhost + ": " + cs);
  286.     req.setRequestHeader("Cookie", cs, false); // "false" because merge syntax breaks Cookie header
  287.   },
  288.   
  289.   
  290.   cookiesCleanup: function(refCookie) {
  291.     var downgraded = [];
  292.  
  293.     var ignored = this.secureCookiesExceptions;
  294.     var disabled = !this.secureCookies;
  295.     var bi = DOM.createBrowserIterator();
  296.     var unsafe, k, c, total, deleted;
  297.     for (var browser; browser = bi.next();) {
  298.       unsafe = this.getUnsafeCookies(browser);
  299.       if (!unsafe) continue;
  300.       total = deleted = 0;
  301.       for (k in unsafe) {
  302.         c = unsafe[k];
  303.         total++;
  304.         if (disabled || (refCookie ? c.belongsTo(refCookie.host) : ignored && ignored.test(c.rawHost))) {
  305.           if (c.exists()) {
  306.             this.log("Cleaning Secure flag from " + c);
  307.             c.secure = false;
  308.             c.save();
  309.           }
  310.           delete unsafe[k];
  311.           deleted++;
  312.         }
  313.       }
  314.       if (total == deleted) this.setUnsafeCookies(browser, null);
  315.       if (!this.cookiesPerTab) break;
  316.     }
  317.   },
  318.   
  319.   get cookiesPerTab() {
  320.     return ns.getPref("secureCookies.perTab", false);
  321.   },
  322.   
  323.   _globalUnsafeCookies: {},
  324.   getUnsafeCookies: function(browser) { 
  325.     return this.cookiesPerTab
  326.       ? browser && ns.getExpando(browser, "unsafeCookies")
  327.       : this._globalUnsafeCookies;
  328.   },
  329.   setUnsafeCookies: function(browser, value) {
  330.     return this.cookiesPerTab
  331.       ? browser && ns.setExpando(browser, "unsafeCookies", value)
  332.       : this._globalUnsafeCookies = value;
  333.   },
  334.   
  335.   shouldForbid: function(site) {
  336.     switch(this.allowHttpsOnly) {
  337.       case 0:
  338.         return false;
  339.       case 1:
  340.         return /^(?:ht|f)tp:\/\//.test(site) && this.isProxied(site);
  341.       case 2:
  342.         return /^(?:ht|f)tp:\/\//.test(site);
  343.     }
  344.     return false;
  345.   },
  346.   
  347.   isProxied: function(u) {
  348.     var ps = CC["@mozilla.org/network/protocol-proxy-service;1"].getService(CI.nsIProtocolProxyService);
  349.    
  350.     this.isProxied = function(u) {
  351.       try {
  352.         if (!(u instanceof CI.nsIURI)) {
  353.           u = IOS.newURI(u, null, null);
  354.         }
  355.         return ps.resolve(u, 0).type != "direct";
  356.       } catch(e) {
  357.         return false;
  358.       }
  359.     }
  360.   },
  361.   
  362.   _getParent: function(req, w) {
  363.     return  w && w.frameElement || DOM.findBrowserForNode(w || IOUtil.findWindow(req));
  364.   }
  365.   
  366. };
  367.  
  368. (function () {
  369.   ["secureCookies", "secureCookiesExceptions", "secureCookiesForced"].forEach(function(p) {
  370.     var v = HTTPS[p];
  371.     delete HTTPS[p];
  372.     HTTPS.__defineGetter__(p, function() {
  373.       return v;
  374.     });
  375.     HTTPS.__defineSetter__(p, function(n) {
  376.       v = n;
  377.       HTTPS.cookiesCleanup();
  378.       return v;
  379.     });
  380.   });
  381. })();
  382.  
  383.  
  384.  
  385.